﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using CommonFramework;
using CommonFramework.Log;
using CommonFramework.Serialize.BytesArray;
using CommonFramework.Time;
using Net.Channel;

namespace Net.Server
{
    public class NetServerManager:NetManagerBase
    {
        private NetSession _tcpServer;

        private Dictionary<Socket, NetSession> _tcpClients = new Dictionary<Socket, NetSession>();
        private object _tcpClientsLock = new object();

        /// <summary>
        /// 启动服务器
        /// </summary>
        public void StartThenLoopForever(int listenPort)
        {
            Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            listenSocket.Bind(new IPEndPoint(IPAddress.Any, listenPort));
            listenSocket.Listen(backlog: 0);

            _tcpServer = new NetSession(listenSocket, this, (session)=>new NetTcpChannel(session));
            ((NetTcpChannel)_tcpServer.Channel).Accept();
            LogUtil.Log("NetServer start succ");

            while (true)
            {
                FrameworkApplication.OnUpdate();
                this.OnUpdate();
                Thread.Sleep(30);
            }
        }

        /// <summary>
        /// 向客户端发送消息包
        /// </summary>
        public void Send(NetSession client, NetPacket packet)
        {
            if (client == null || packet == null || !client.IsConnected())
            {
                LogUtil.LogError("invalid params");
                return;
            }
            client.Send(packet);
        }
        public void Broadcast(NetPacket packet)
        {
            NetSession[] clients = GetAllClientsArray();
            foreach (var client in clients)
            {
                if (!client.IsClosed())
                    Send(client, packet);
            }
        }

        [ThreadStatic] private UdpClient _udpBroadcastServ;
        public async void UpdBroadcast(NetPacket packet)
        {
            if (_udpBroadcastServ == null)
                _udpBroadcastServ = new UdpClient();

            BytesBuffer sendBuffer = NetUtil.GetBytesFromPacket(packet);
            byte[] sendBytes= sendBuffer.ToBytes();
            int toSendCount = sendBytes.Length;
            if (toSendCount > NetUtil.MAX_UDP_PACKET_SIZE)
            {
                LogUtil.LogError("Udp数据包过大,拒绝发送");
                return;
            }

            int sendFailed = 0;
            NetSession[] clients = GetAllClientsArray();
            foreach (var client in clients)
            {
                if (client.IsClosed())
                    continue;
                try
                {
                    int sendCount = 0;
                    while (sendCount < toSendCount)
                    {
                        Task<int> sendTask = _udpBroadcastServ.SendAsync(sendBytes, toSendCount, client.RemoteHost, client.RemotePort);
                        sendCount += await sendTask.ConfigureAwait(true);
                        sendFailed++;

                        if (sendFailed > 3)
                            break;
                    }
                    sendFailed = 0;
                }
                catch (Exception e)
                {
                    LogUtil.LogError("UpdBroadcast failed",e);
                }
            }
        }

        public List<NetSession> GetAllClientsList()
        {
            lock (_tcpClientsLock)
            {
                return _tcpClients.Values.ToList();
            }
        }

        public NetSession[] GetAllClientsArray()
        {
            lock (_tcpClientsLock)
            {
                return _tcpClients.Values.ToArray();
            }
        }

        public override void Close(Socket clientSocket)
        {
            NetSession closeSession = null;

            //从clients容器中移除
            lock (_tcpClientsLock)
            {
                if (_tcpClients.TryGetValue(clientSocket, out closeSession))
                    _tcpClients.Remove(clientSocket);
            }

            if (closeSession!=null)
            {
                NetEventHandler.OnDisconnect(closeSession);
                closeSession.Close();
            }
            else
            {
                //关闭Socket
                clientSocket.Close();
            }
        }

        private void OnUpdate()
        {
            CheckPingUpdate();
        }

        public override void OnAccept(NetSession session)
        {
            lock (_tcpClientsLock)
            {
                _tcpClients[session.Socket] = session;
            }
            session.LastHeartPingTime = TimeUtil.NowTimestampSeconds();
            ((NetTcpChannel)session.Channel).BeginReceive();
        }

        public override void OnReceivePacket(NetSession session, NetPacket packet)
        {
            NetPacketHandler.OnNetPacket(session, packet);
        }

        private void CheckPingUpdate()
        {
            if (!IsUsingPing)
                return;

            long now = TimeUtil.NowTimestampSeconds();
            NetSession[] clients = GetAllClientsArray();
            foreach (var client in clients)
            {
                if (now - client.LastHeartPingTime >= 4 * PingInterval)
                {
                    LogUtil.LogWarning($"client({client.Socket.RemoteEndPoint}) close because of ping timeout");
                    Close(client.Socket);
                    break;
                }
            }
        }
    }
}